home *** CD-ROM | disk | FTP | other *** search
- /*
- * Software License Agreement (BSD License)
- *
- * Copyright (c) 2007, Parakey Inc.
- * All rights reserved.
- *
- * Redistribution and use of this software in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the
- * following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the
- * following disclaimer in the documentation and/or other
- * materials provided with the distribution.
- *
- * * Neither the name of Parakey Inc. nor the names of its
- * contributors may be used to endorse or promote products
- * derived from this software without specific prior
- * written permission of Parakey Inc.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- /*
- * Creator:
- * Joe Hewitt
- * Contributors
- * John J. Barton (IBM Almaden)
- * Jan Odvarko (Mozilla Corp.)
- * Max Stepanov (Aptana Inc.)
- * Rob Campbell (Mozilla Corp.)
- * Hans Hillen (Paciello Group, Mozilla)
- * Curtis Bartley (Mozilla Corp.)
- * Mike Collins (IBM Almaden)
- * Kevin Decker
- * Mike Ratcliffe (Comartis AG)
- * Hernan Rodríguez Colmeiro
- * Austin Andrews
- * Christoph Dorn
- * Steven Roussey (AppCenter Inc, Network54)
- */
-
- ///////////////////////////////////////////////////////////////////////////
- //// InsideOutBox
-
- /**
- * InsideOutBoxView is a simple interface definition for views implementing
- * InsideOutBox controls. All implementors must define these methods.
- * Implemented in InspectorUI.
- */
-
- /*
- InsideOutBoxView = {
- //
- * Retrieves the parent object for a given child object.
- * @param aChild
- * The child node to retrieve the parent object for.
- * @returns a DOM node | null
- //
- getParentObject: function(aChild) {},
-
- //
- * Retrieves a given child node.
- *
- * If both index and previousSibling are passed, the implementation
- * may assume that previousSibling will be the return for getChildObject
- * with index-1.
- * @param aParent
- * The parent object of the child object to retrieve.
- * @param aIndex
- * The index of the child object to retrieve from aParent.
- * @param aPreviousSibling
- * The previous sibling of the child object to retrieve.
- * Supercedes aIndex.
- * @returns a DOM object | null
- //
- getChildObject: function(aParent, aIndex, aPreviousSibling) {},
-
- //
- * Renders the HTML representation of the object. Should return an HTML
- * object which will be displayed to the user.
- * @param aObject
- * The object to create the box object for.
- * @param aIsRoot
- * Is the object the root object. May not be used in all
- * implementations.
- * @returns an object box | null
- //
- createObjectBox: function(aObject, aIsRoot) {},
-
- //
- * Convenience wrappers for classList API.
- * @param aObject
- * DOM node to query/set.
- * @param aClassName
- * String containing the class name to query/set.
- //
- hasClass: function(aObject, aClassName) {},
- addClass: function(aObject, aClassName) {},
- removeClass: function(aObject, aClassName) {}
- };
- */
-
- /**
- * Creates a tree based on objects provided by a separate "view" object.
- *
- * Construction uses an "inside-out" algorithm, meaning that the view's job is
- * first to tell us the ancestry of each object, and secondarily its
- * descendants.
- *
- * Constructor
- * @param aView
- * The view requiring the InsideOutBox.
- * @param aBox
- * The box object containing the InsideOutBox. Required to add/remove
- * children during box manipulation (toggling opened or closed).
- */
-
- var EXPORTED_SYMBOLS = ["InsideOutBox"];
-
- function InsideOutBox(aView, aBox)
- {
- this.view = aView;
- this.box = aBox;
-
- this.rootObject = null;
-
- this.rootObjectBox = null;
- this.selectedObjectBox = null;
- this.highlightedObjectBox = null;
- this.scrollIntoView = false;
- };
-
- InsideOutBox.prototype =
- {
- /**
- * Highlight the given object node in the tree.
- * @param aObject
- * the object to highlight.
- * @returns objectBox
- */
- highlight: function IOBox_highlight(aObject)
- {
- let objectBox = this.createObjectBox(aObject);
- this.highlightObjectBox(objectBox);
- return objectBox;
- },
-
- /**
- * Open the given object node in the tree.
- * @param aObject
- * The object node to open.
- * @returns objectBox
- */
- openObject: function IOBox_openObject(aObject)
- {
- let object = aObject;
- let firstChild = this.view.getChildObject(object, 0);
- if (firstChild)
- object = firstChild;
-
- return this.openToObject(object);
- },
-
- /**
- * Open the tree up to the given object node.
- * @param aObject
- * The object in the tree to open to.
- * @returns objectBox
- */
- openToObject: function IOBox_openToObject(aObject)
- {
- let objectBox = this.createObjectBox(aObject);
- this.openObjectBox(objectBox);
- return objectBox;
- },
-
- /**
- * Select the given object node in the tree.
- * @param aObject
- * The object node to select.
- * @param makeBoxVisible
- * Boolean. Open the object box in the tree?
- * @param forceOpen
- * Force the object box open by expanding all elements in the tree?
- * @param scrollIntoView
- * Scroll the objectBox into view?
- * @returns nsIDOMNode|null
- * A DOM node that represents the "object box", the element that
- * holds/displays the given aObject representation in the tree. If
- * the object cannot be selected, if it is a stale object, null is
- * returned.
- */
- select:
- function IOBox_select(aObject, makeBoxVisible, forceOpen, scrollIntoView)
- {
- let objectBox = this.createObjectBox(aObject);
- if (!objectBox) {
- return null;
- }
- this.selectObjectBox(objectBox, forceOpen);
- if (makeBoxVisible) {
- this.openObjectBox(objectBox);
- if (scrollIntoView) {
- objectBox.scrollIntoView(true);
- }
- }
- return objectBox;
- },
-
- /**
- * Expands/contracts the given object, depending on its state.
- * @param aObject
- * The tree node to expand/contract.
- */
- toggleObject: function IOBox_toggleObject(aObject)
- {
- let box = this.createObjectBox(aObject);
- if (!(this.view.hasClass(box, "open")))
- this.expandObjectBox(box);
- else
- this.contractObjectBox(box);
- },
-
- /**
- * Expand the given object in the tree.
- * @param aObject
- * The tree node to expand.
- */
- expandObject: function IOBox_expandObject(aObject)
- {
- let objectBox = this.createObjectBox(aObject);
- if (objectBox)
- this.expandObjectBox(objectBox);
- },
-
- /**
- * Contract the given object in the tree.
- * @param aObject
- * The tree node to contract.
- */
- contractObject: function IOBox_contractObject(aObject)
- {
- let objectBox = this.createObjectBox(aObject);
- if (objectBox)
- this.contractObjectBox(objectBox);
- },
-
- /**
- * General method for iterating over an object's ancestors and performing
- * some function.
- * @param aObject
- * The object whose ancestors we wish to iterate over.
- * @param aCallback
- * The function to call with the object as argument.
- */
-
- iterateObjectAncestors: function IOBox_iterateObjectAncesors(aObject, aCallback)
- {
- let object = aObject;
- if (!(aCallback && typeof(aCallback) == "function")) {
- this.view._log("Illegal argument in IOBox.iterateObjectAncestors");
- return;
- }
- while ((object = this.getParentObjectBox(object)))
- aCallback(object);
- },
-
- /**
- * Highlight the given objectBox in the tree.
- * @param aObjectBox
- * The objectBox to highlight.
- */
- highlightObjectBox: function IOBox_highlightObjectBox(aObjectBox)
- {
- let self = this;
-
- if (!aObjectBox)
- return;
-
- if (this.highlightedObjectBox) {
- this.view.removeClass(this.highlightedObjectBox, "highlighted");
- this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
- self.view.removeClass(box, "highlightOpen");
- });
- }
-
- this.highlightedObjectBox = aObjectBox;
-
- this.view.addClass(aObjectBox, "highlighted");
- this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
- self.view.addClass(box, "highlightOpen");
- });
-
- aObjectBox.scrollIntoView(true);
- },
-
- /**
- * Select the given objectBox in the tree, forcing it to be open if necessary.
- * @param aObjectBox
- * The objectBox to select.
- * @param forceOpen
- * Force the box (subtree) to be open?
- */
- selectObjectBox: function IOBox_selectObjectBox(aObjectBox, forceOpen)
- {
- let isSelected = this.selectedObjectBox &&
- aObjectBox == this.selectedObjectBox;
-
- // aObjectBox is already selected, return
- if (isSelected)
- return;
-
- if (this.selectedObjectBox)
- this.view.removeClass(this.selectedObjectBox, "selected");
-
- this.selectedObjectBox = aObjectBox;
-
- if (aObjectBox) {
- this.view.addClass(aObjectBox, "selected");
-
- // Force it open the first time it is selected
- if (forceOpen)
- this.expandObjectBox(aObjectBox, true);
- }
- },
-
- /**
- * Open the ancestors of the given object box.
- * @param aObjectBox
- * The object box to open.
- */
- openObjectBox: function IOBox_openObjectBox(aObjectBox)
- {
- if (!aObjectBox)
- return;
-
- let self = this;
- this.iterateObjectAncestors(aObjectBox, function (box) {
- self.view.addClass(box, "open");
- let labelBox = box.querySelector(".nodeLabelBox");
- if (labelBox)
- labelBox.setAttribute("aria-expanded", "true");
- });
- },
-
- /**
- * Expand the given object box.
- * @param aObjectBox
- * The object box to expand.
- */
- expandObjectBox: function IOBox_expandObjectBox(aObjectBox)
- {
- let nodeChildBox = this.getChildObjectBox(aObjectBox);
-
- // no children means nothing to expand, return
- if (!nodeChildBox)
- return;
-
- if (!aObjectBox.populated) {
- let firstChild = this.view.getChildObject(aObjectBox.repObject, 0);
- this.populateChildBox(firstChild, nodeChildBox);
- }
- let labelBox = aObjectBox.querySelector(".nodeLabelBox");
- if (labelBox)
- labelBox.setAttribute("aria-expanded", "true");
- this.view.addClass(aObjectBox, "open");
- },
-
- /**
- * Contract the given object box.
- * @param aObjectBox
- * The object box to contract.
- */
- contractObjectBox: function IOBox_contractObjectBox(aObjectBox)
- {
- this.view.removeClass(aObjectBox, "open");
- let nodeLabel = aObjectBox.querySelector(".nodeLabel");
- let labelBox = nodeLabel.querySelector(".nodeLabelBox");
- if (labelBox)
- labelBox.setAttribute("aria-expanded", "false");
- },
-
- /**
- * Toggle the given object box, forcing open if requested.
- * @param aObjectBox
- * The object box to toggle.
- * @param forceOpen
- * Force the objectbox open?
- */
- toggleObjectBox: function IOBox_toggleObjectBox(aObjectBox, forceOpen)
- {
- let isOpen = this.view.hasClass(aObjectBox, "open");
-
- if (!forceOpen && isOpen)
- this.contractObjectBox(aObjectBox);
- else if (!isOpen)
- this.expandObjectBox(aObjectBox);
- },
-
- /**
- * Creates all of the boxes for an object, its ancestors, and siblings.
- * @param aObject
- * The tree node to create the object boxes for.
- * @returns anObjectBox or null
- */
- createObjectBox: function IOBox_createObjectBox(aObject)
- {
- if (!aObject)
- return null;
-
- this.rootObject = this.getRootNode(aObject) || aObject;
-
- // Get or create all of the boxes for the target and its ancestors
- let objectBox = this.createObjectBoxes(aObject, this.rootObject);
-
- if (!objectBox)
- return null;
-
- if (aObject == this.rootObject)
- return objectBox;
-
- return this.populateChildBox(aObject, objectBox.parentNode);
- },
-
- /**
- * Creates all of the boxes for an object, its ancestors, and siblings up to
- * a root.
- * @param aObject
- * The tree's object node to create the object boxes for.
- * @param aRootObject
- * The root object at which to stop building object boxes.
- * @returns an object box or null
- */
- createObjectBoxes: function IOBox_createObjectBoxes(aObject, aRootObject)
- {
- if (!aObject)
- return null;
-
- if (aObject == aRootObject) {
- if (!this.rootObjectBox || this.rootObjectBox.repObject != aRootObject) {
- if (this.rootObjectBox) {
- try {
- this.box.removeChild(this.rootObjectBox);
- } catch (exc) {
- this.view._log("this.box.removeChild(this.rootObjectBox) FAILS " +
- this.box + " must not contain " + this.rootObjectBox);
- }
- }
-
- this.highlightedObjectBox = null;
- this.selectedObjectBox = null;
- this.rootObjectBox = this.view.createObjectBox(aObject, true);
- this.box.appendChild(this.rootObjectBox);
- }
- return this.rootObjectBox;
- }
-
- let parentNode = this.view.getParentObject(aObject);
- let parentObjectBox = this.createObjectBoxes(parentNode, aRootObject);
-
- if (!parentObjectBox)
- return null;
-
- let parentChildBox = this.getChildObjectBox(parentObjectBox);
-
- if (!parentChildBox)
- return null;
-
- let childObjectBox = this.findChildObjectBox(parentChildBox, aObject);
-
- return childObjectBox ? childObjectBox
- : this.populateChildBox(aObject, parentChildBox);
- },
-
- /**
- * Locate the object box for a given object node.
- * @param aObject
- * The given object node in the tree.
- * @returns an object box or null.
- */
- findObjectBox: function IOBox_findObjectBox(aObject)
- {
- if (!aObject)
- return null;
-
- if (aObject == this.rootObject)
- return this.rootObjectBox;
-
- let parentNode = this.view.getParentObject(aObject);
- let parentObjectBox = this.findObjectBox(parentNode);
- if (!parentObjectBox)
- return null;
-
- let parentChildBox = this.getChildObjectBox(parentObjectBox);
- if (!parentChildBox)
- return null;
-
- return this.findChildObjectBox(parentChildBox, aObject);
- },
-
- getAncestorByClass: function IOBox_getAncestorByClass(node, className)
- {
- for (let parent = node; parent; parent = parent.parentNode) {
- if (this.view.hasClass(parent, className))
- return parent;
- }
-
- return null;
- },
-
- /**
- * We want all children of the parent of repObject.
- */
- populateChildBox: function IOBox_populateChildBox(repObject, nodeChildBox)
- {
- if (!repObject)
- return null;
-
- let parentObjectBox = this.getAncestorByClass(nodeChildBox, "nodeBox");
-
- if (parentObjectBox.populated)
- return this.findChildObjectBox(nodeChildBox, repObject);
-
- let lastSiblingBox = this.getChildObjectBox(nodeChildBox);
- let siblingBox = nodeChildBox.firstChild;
- let targetBox = null;
- let view = this.view;
- let targetSibling = null;
- let parentNode = view.getParentObject(repObject);
-
- for (let i = 0; 1; ++i) {
- targetSibling = view.getChildObject(parentNode, i, targetSibling);
- if (!targetSibling)
- break;
-
- // Check if we need to start appending, or continue to insert before
- if (lastSiblingBox && lastSiblingBox.repObject == targetSibling)
- lastSiblingBox = null;
-
- if (!siblingBox || siblingBox.repObject != targetSibling) {
- let newBox = view.createObjectBox(targetSibling);
- if (newBox) {
- if (lastSiblingBox)
- nodeChildBox.insertBefore(newBox, lastSiblingBox);
- else
- nodeChildBox.appendChild(newBox);
- }
-
- siblingBox = newBox;
- }
-
- if (targetSibling == repObject)
- targetBox = siblingBox;
-
- if (siblingBox && siblingBox.repObject == targetSibling)
- siblingBox = siblingBox.nextSibling;
- }
-
- if (targetBox)
- parentObjectBox.populated = true;
-
- return targetBox;
- },
-
- /**
- * Get the parent object box of a given object box.
- * @params aObjectBox
- * The object box of the parent.
- * @returns an object box or null
- */
- getParentObjectBox: function IOBox_getParentObjectBox(aObjectBox)
- {
- let parent = aObjectBox.parentNode ? aObjectBox.parentNode.parentNode : null;
- return parent && parent.repObject ? parent : null;
- },
-
- /**
- * Get the child object box of a given object box.
- * @param aObjectBox
- * The object box whose child you want.
- * @returns an object box or null
- */
- getChildObjectBox: function IOBox_getChildObjectBox(aObjectBox)
- {
- return aObjectBox.querySelector(".nodeChildBox");
- },
-
- /**
- * Find the child object box for a given repObject within the subtree
- * rooted at aParentNodeBox.
- * @param aParentNodeBox
- * root of the subtree in which to search for repObject.
- * @param aRepObject
- * The object you wish to locate in the subtree.
- * @returns an object box or null
- */
- findChildObjectBox: function IOBox_findChildObjectBox(aParentNodeBox, aRepObject)
- {
- let childBox = aParentNodeBox.firstChild;
- while (childBox) {
- if (childBox.repObject == aRepObject)
- return childBox;
- childBox = childBox.nextSibling;
- }
- return null; // not found
- },
-
- /**
- * Determines if the given node is an ancestor of the current root.
- * @param aNode
- * The node to look for within the tree.
- * @returns boolean
- */
- isInExistingRoot: function IOBox_isInExistingRoot(aNode)
- {
- let parentNode = aNode;
- while (parentNode && parentNode != this.rootObject) {
- parentNode = this.view.getParentObject(parentNode);
- }
- return parentNode == this.rootObject;
- },
-
- /**
- * Get the root node of a given node.
- * @param aNode
- * The node whose root you wish to retrieve.
- * @returns a root node or null
- */
- getRootNode: function IOBox_getRootNode(aNode)
- {
- let node = aNode;
- let tmpNode;
- while ((tmpNode = this.view.getParentObject(node)))
- node = tmpNode;
-
- return node;
- },
-
- /**
- * Clean up our mess.
- */
- destroy: function IOBox_destroy()
- {
- delete this.view;
- delete this.box;
- delete this.rootObject;
- delete this.rootObjectBox;
- delete this.selectedObjectBox;
- delete this.highlightedObjectBox;
- delete this.scrollIntoView;
- }
- };
-